<?php
/**
 * @see Epic_Db_Adapter_Interface
 */
require 'Epic/Db/Adapter/Interface.php';

/**
 * @category   Epic
 * @package    Epic_Db
 * @author     Pham Ngoc Khanh <musonepnk@gmail.com>
 */
class Epic_Db_Adapter_Mysqli implements Epic_Db_Adapter_Interface
{  
    /**
	 * Parameter array for connecting database
	 * @var array
	 */
    private $_config = array();
    
    /**
     * Resource connect
     * @var resource
     */
    private $_conn = null;

    /**
     * The resulting array returned from the query
     * @var array
     */
    private $_result = null;
    
    /**
     * Singleton instance
     * @var Epic_Db_Adapter_Mysqli
     */
    private static $_instance = null;

	/**
     * Singleton instance
     *
     * @param  array $config
     * @return Epic_Db_Adapter_Mysqli
     */
	public static function getInstance(array $config = array()) 
	{
        if (null === self::$_instance) {
            self::$_instance = new self($config);
        }
        return self::$_instance;
	}
	
    /**
     * Enforce singleton; disallow cloning
     *
     * @return void
     */
    private function __clone() {}
	
    /**
     * Constructor
     *
     * Instantiate using {@link getInstance()}; Epic_Db_Adapter_Mysqli is a singleton object.
     *
     * @param array $config
     * @return void
     */
    private function __construct(array $config)
    {
        if (count($config) != 4) {
            throw new Epic_Db_Adapter_Exception('Invalid number of connection parameters');  
        }
        $this->_config = $config;
    }
   
    /**
     * Connect to MySQL
     * 
     * @return void
     */
    public function connect()
    {
        // connect only once
        if ($this->_conn === null) {
            list($host, $user, $password, $database) = $this->_config;
            if (!$this->_conn = mysqli_connect($host, $user, $password, $database)) {
                throw new Epic_Db_Adapter_Exception('Error connect MySQL: ' . mysqli_connect_error());
            }
            mysqli_query($this->_conn, "SET NAMES 'UTF8'");
            unset($host, $user, $password, $database);      
        }
    }

    /**
     * Execute the specified query
     * 
     * @param string $query
     * @return array|null The resulting array returned from the query
     */
    public function query($query)
    {
        if (!is_string($query) || empty($query)) {
            throw new Epic_Db_Adapter_Exception('The specified query is not valid.');
        }
        // lazy connect to MySQL
        $this->connect();
        if (!$this->_result = mysqli_query($this->_conn, $query)) {
        	throw new Epic_Db_Adapter_Exception('Error performing query ' . $query . ' Error : ' . mysqli_error($this->_conn));
        }
        return $this;
    }
    
    /**
     * Fetch a single row from the current result set
     *
     * @return null|array $row retrieve form database
     */
    public function fetchOne()
    {
    	if ($this->_result !== null && $row = mysqli_fetch_array($this->_result, MYSQLI_ASSOC)) {
            return $row;
    	}
    	return null;
    }
    
    /**
     * Fetch all rows from the current result set
     *
     * @return null|array $rows retrieve form database
     */
    public function fetchAll()
    {
    	if ($this->_result !== null) {
    		$rows = array();
    		while ($row = mysqli_fetch_array($this->_result, MYSQLI_ASSOC)) {
    			$rows[] = $row;
    		}
    		return $rows;
    	}
    	return null;
    }
    
    /**
     * Perform a SELECT statement
     * 
     * @param string $table
     * @param string $where
     * @param string $fields
     * @param int|null $limit
     * @param int|null $offset
     * @return int number of row 
     */
    public function select($table = '', $where = '', $fields = '*', $order = '', $limit = null, $offset = null)
    {
    	$query = 'SELECT ' . $fields . ' FROM ' . $table
    	. (($where) ? ' WHERE ' . $where : '')
    	. (($limit) ? ' LIMIT ' . $limit : '')
    	. (($offset && $limit) ? ' OFFSET ' . $offset : '')
    	. (($order) ? ' ORDER BY ' . $order : '');
    	$this->query($query);
    	return $this->countRows();
    }
   
    /**
     * Perform an INSERT statement
     * 
     * @param string $table
     * @param array $data
     * @return int id insert
     */ 
    public function insert($table, array $data)
    {
        $fields = implode(',', array_keys($data));
        $values = implode(',', array_map(array($this, '_quote'), array_values($data)));
        $query = 'INSERT INTO ' . $table . '(' . $fields . ')' . ' VALUES (' . $values . ')';
        $this->query($query);
        return $this->getInsertId();
    }

    /**
     * Perform an UPDATE statement
     * 
     * @param string $table
     * @param array  $data
     * @param string $where
     * @return int rows affected 
     */
    public function update($table, array $data, $where = '')
    {
    	$set = array();
    	foreach ($data as $field => $value) {
    		$set[] = $field . '=' . $this->_quote($value);
    	}
    	$set = implode(',', $set);
    	$query = 'UPDATE ' . $table . ' SET ' . $set
    	. (($where) ? ' WHERE ' . $where : '');
    	$this->query($query);
    	return $this->getAffectedRows();
    }
   
    /**
     * Perform a DELETE statement
     *
     * @param string $table
     * @param string $where
     * @return int number of affected rows
     */
    public function delete($table, $where = '')
    {
        $query = 'DELETE FROM ' . $table
               . (($where) ? ' WHERE ' . $where : '');
        $this->query($query);
        return $this->getAffectedRows();
    }
    
    /**
     * Single quote the specified value
     * Characters encoded are NUL (ASCII 0), \n, \r, \, ', ", and Control-Z.
     *
     * @param string $value The string to be escaped
     * @return an escaped string
     */
    private function _quote($value)
    {
    	if ($value === null) {
    		$value = 'NULL';
    	} else if (!is_numeric($value)) {
    		// lazy connect MySQL
    		$this->connect();
    		$value = '\'' . mysqli_real_escape_string($this->_conn, $value) . '\'';
    	}
    	return $value;
    }
    
    /**
     * Single quote the specified value
     * Characters encoded are NUL (ASCII 0), \n, \r, \, ', ", and Control-Z. 
     * 
     * @param string $value The string to be escaped
     * @return an escaped string  
     */
    public function quote($value)
    {
        if ($value === null) {
            $value = 'NULL';
        } else if (!is_numeric($value)) {
        	// lazy connect MySQL
        	$this->connect();
            $value = mysqli_real_escape_string($this->_conn, $value);
        }
        return $value;
    }
    
    /**
     * Get the insertion ID
     *     
     * @return null|int id insert 
     */
    public function getInsertId()
    {
    	return $this->_conn !== null ? mysqli_insert_id($this->_conn) : null;
    }

    /**
     * Get the number of rows returned by the current result set
     * 
     * @return int number of row 
     */
    public function countRows()
    {
    	return $this->_result !== null ? mysqli_num_rows($this->_result) : 0;
    }

    /**
     * Get the number of affected rows
     * 
     * @return int number of affected rows
     */
    public function getAffectedRows()
    {
    	return $this->_conn !== null ? mysqli_affected_rows($this->_conn) : 0;
    }
      
    /**
     * Close explicitly the database connection
     * 
     * @return boolean
     */
    public function disconnect()
    {
    	if ($this->_conn !== null) {
    		mysqli_close($this->_conn);
    		$this->_conn = null;
    		return true;
    	}
    	return false;
    }
}